/*
 * Copyright (C) 2020, All rights Reserved, Designed By
 * @author: maoyz
 * @Copyright: 2020-06-23 10:01  Inc. All rights reserved.
 */
package com.myz.design.aop;

import cn.hutool.core.util.ClassUtil;
import com.myz.design.aop.aspect.Aspect;
import com.myz.design.aop.proxy.ProxyFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;


/**
 * @author maoyz
 */
public final class ProxyUtil {

    /**
     * @param target      目标对象
     * @param aspectClass 切面对象类
     * @param <T>         被代理对象类型
     * @return 代理对象
     */
    public static <T> T proxy(T target, Class<? extends Aspect> aspectClass) {
        return ProxyFactory.createProxy(target, aspectClass);
    }

    /**
     * @param target 目标对象
     * @param aspect 切面对象
     * @param <T>    被代理对象类型
     * @return 代理对象
     */
    public static <T> T proxy(T target, Aspect aspect) {
        return ProxyFactory.createProxy(target, aspect);
    }

    /**
     * 创建动态代理对象
     * 动态代理对象的创建原理是：
     * 假设创建的代理对象名为 $Proxy0
     * 1、根据传入的interfaces动态生成一个类，实现interfaces中的接口
     * 2、通过传入的classloader将刚生成的类加载到jvm中。即将$Proxy0类load
     * 3、调用$Proxy0的$Proxy0(InvocationHandler)构造函数 创建$Proxy0的对象，并且用interfaces参数遍历其所有接口的方法，这些实现方法的实现本质上是通过反射调用被代理对象的方法
     * 4、将$Proxy0的实例返回给客户端。
     * 5、当调用代理类的相应方法时，相当于调用 {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])} 方法
     *
     * @param <T>               被代理对象类型
     * @param classloader       被代理类对应的ClassLoader
     * @param invocationHandler {@link InvocationHandler} ，被代理类通过实现此接口提供动态代理功能
     * @param interfaces        代理类中需要实现的被代理类的接口方法
     * @return 代理类
     */
    @SuppressWarnings("unchecked")
    public static <T> T newProxyInstance(ClassLoader classloader, java.lang.reflect.InvocationHandler invocationHandler, Class<?>... interfaces) {
        return (T) Proxy.newProxyInstance(classloader, interfaces, invocationHandler);
    }

    /**
     * 创建动态代理对象
     *
     * @param <T>               被代理对象类型
     * @param invocationHandler 被代理类通过实现此接口提供动态代理功能
     * @param interfaces        代理类中需要实现的被代理类的接口方法
     * @return 代理类
     */
    public static <T> T newProxyInstance(java.lang.reflect.InvocationHandler invocationHandler, Class<?>... interfaces) {
        return newProxyInstance(ClassUtil.getClassLoader(), invocationHandler, interfaces);
    }
}